#include "Input.h"
#include "Util.h"

namespace TestDX
{

Input::Input() :
	_pDI(NULL),
	_pKeyboard(NULL),
	_pMouse(NULL),
	_mouseDX(0),
	_mouseDY(0)
{
}


Input::~Input()
{
	Shutdown();
}

bool Input::Init(HINSTANCE hinstance, HWND hwnd)
{
	// Zeroing keys state
	ZeroMemory(_keyboardState, sizeof(_keyboardState));

	_mouseDX = 0;
	_mouseDY = 0;

	HR(DirectInput8Create(hinstance,
						  DIRECTINPUT_VERSION,
						  IID_IDirectInput8,
						  (void **) &_pDI,
						  NULL));

	// Initialize direct input interface for the keyboard
	HR(_pDI->CreateDevice(GUID_SysKeyboard, &_pKeyboard, NULL));
	// Set data format (we use default data format)
	HR(_pKeyboard->SetDataFormat(&c_dfDIKeyboard));
	// Set cooperative level of the keyboard to not share with other programs
	HR(_pKeyboard->SetCooperativeLevel(hwnd, DISCL_FOREGROUND | DISCL_EXCLUSIVE));
	// And finally acquire keyboard
	HR(_pKeyboard->Acquire());

	// Initialize direct input interface for the mouse
	HR(_pDI->CreateDevice(GUID_SysMouse, &_pMouse, NULL));
	// Set data format (default one)
	HR(_pMouse->SetDataFormat(&c_dfDIMouse));
	// Set cooperative level of the keyboard to share with other programs
	HR(_pMouse->SetCooperativeLevel(hwnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE));
	// And acquire the mouse
	HR(_pMouse->Acquire());

	return true;
}

void Input::Shutdown()
{
	if(_pMouse)
	{
		_pMouse->Unacquire();
		RELEASE_COM(_pMouse);
	}

	if(_pKeyboard)
	{
		_pKeyboard->Unacquire();
		RELEASE_COM(_pKeyboard);
	}

	RELEASE_COM(_pDI);
}

bool Input::Frame()
{
	if(!ReadKeyboard())
	{
		// @ TODO: Log
		return false;
	}

	if(!ReadMouse())
	{
		// @ TODO: Log
		return false;
	}

	ProcessInput();

	return true;
}

bool Input::ReadKeyboard()
{
	HRESULT hr = _pKeyboard->GetDeviceState(
		sizeof(_keyboardState), (LPVOID)&_keyboardState);

	if(FAILED(hr))
	{
		if(hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED)
			_pKeyboard->Acquire();
		else
			return false;
	}

	return true;
}

bool Input::ReadMouse()
{
	HRESULT hr = _pMouse->GetDeviceState(sizeof(_mouseState),
										 (LPVOID)&_mouseState);

	if(FAILED(hr))
	{
		if(hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED)
			_pMouse->Acquire();
		else
			return false;
	}

	return true;
}

void Input::ProcessInput()
{
	// Update mouse movement
	_mouseDX += _mouseState.lX;
	_mouseDY += _mouseState.lY;
}

bool Input::IsEscapePressed()
{
	return _keyboardState[DIK_ESCAPE] & 0x80;
}

void Input::GetMouseMovement(int & mX, int & mY)
{
	mX = _mouseDX;
	mY = _mouseDY;

	// Reset movement until next read
	_mouseDX = 0;
	_mouseDY = 0;
}

}